package com.ccrfid.rmc6.service.report;

import com.ccrfid.rmc6.entity.business.entrance.PersonnelAttendanceRecordEntity;
import com.ccrfid.rmc6.entity.personnel.PersonnelMoveEntity;
import com.ccrfid.rmc6.entity.personnel.PersonnelView;
import com.ccrfid.rmc6.entity.poi.PoiLocationEntity;
import com.ccrfid.rmc6.entity.sys.DictEntity;
import com.ccrfid.rmc6.pojo.dto.MoveInLocationDto;
import com.ccrfid.rmc6.pojo.dto.PersonelAllTypeCountDto;
import com.ccrfid.rmc6.pojo.dto.PersonnelMoveDto;
import com.ccrfid.rmc6.pojo.dto.personnel.PersonnelLocatorDto;
import com.ccrfid.rmc6.pojo.dto.report.*;
import com.ccrfid.rmc6.repository.alarm.AlarmNotifyRecordVeiwRepository;
import com.ccrfid.rmc6.repository.alarm.AlarmRecordRepository;
import com.ccrfid.rmc6.repository.business.entrance.PersonnelAttendanceRecordRepository;
import com.ccrfid.rmc6.repository.personnel.PersonnelMoveRepository;
import com.ccrfid.rmc6.repository.personnel.PersonnelViewRepository;
import com.ccrfid.rmc6.repository.poi.PoiLocationRepository;
import com.ccrfid.rmc6.repository.sys.DictRepository;
import com.ccrfid.rmc6.service.personnel.SyncPrisonerServiceImpl;
import com.ccrfid.rmc6.service.sys.DictService;
import com.ccrfid.rmc6.service.sys.RoomService;
import com.ccrfid.rmc6.service.sys.RoomServiceImpl;
import com.ccrfid.rmc6.util.DateUtil;
import com.ccrfid.rmc6.util.GeomUtil;
import org.checkerframework.checker.units.qual.A;
import org.eclipse.jetty.util.thread.TimerScheduler;
import org.hibernate.query.NativeQuery;
import org.hibernate.transform.Transformers;
import org.hibernate.type.IntegerType;
import org.hibernate.type.LongType;
import org.hibernate.type.StringType;
import org.hibernate.type.TimestampType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Service
@Transactional
public class ReportServiceImpl implements ReportService {

    private static final Logger logger = LoggerFactory.getLogger(ReportServiceImpl.class);

    @Autowired
    private PoiLocationRepository poiLocationRepository;
    @Autowired
    private PersonnelViewRepository personnelViewRepository;
    @Autowired
    private PersonnelMoveRepository personnelMoveRepository;
    @Autowired
    private AlarmRecordRepository alarmRecordRepository;
    @Autowired
    private RoomService roomService;
    @Autowired
    private PersonnelAttendanceRecordRepository personnelAttendanceRecordRepository;

    @PersistenceContext
    private EntityManager entityManager;
    @Autowired
    private DictService dictService;

    @Value("${work.time}")
    private String workTime;

    @Value("${work.poi}")
    private String workPoi;
    @Override
    public List<LocationPersonDto> exportLocationReport(){
        List<PoiLocationEntity> poiLocationEntityList = poiLocationRepository.getAllByMapLevel(1);
        List<LocationPersonDto> locationPersonDtoList = new ArrayList<>();
        for (PoiLocationEntity poiLocationEntity : poiLocationEntityList){
            LocationPersonDto locationPersonDto = new LocationPersonDto();
            locationPersonDto.setLocationId(poiLocationEntity.getId());
            locationPersonDto.setLocationName(poiLocationEntity.getLocationName());
            List<PersonelAllTypeCountDto> personelAllTypeCountDtoList = new ArrayList<>();
            PersonelAllTypeCountDto zyCountDto = new PersonelAllTypeCountDto(101,"在押人员",getLocationPersonCount(poiLocationEntity.getId(), 101));
            PersonelAllTypeCountDto gzCountDto = new PersonelAllTypeCountDto(102,"工作人员",getLocationPersonCount(poiLocationEntity.getId(), 102));
            PersonelAllTypeCountDto wlCountDto = new PersonelAllTypeCountDto(103,"外来人员",getLocationPersonCount(poiLocationEntity.getId(), 103));
            personelAllTypeCountDtoList.add(zyCountDto);
            personelAllTypeCountDtoList.add(gzCountDto);
            personelAllTypeCountDtoList.add(wlCountDto);
            locationPersonDto.setPersonelAllTypeCountDtoList(personelAllTypeCountDtoList);
            locationPersonDtoList.add(locationPersonDto);
        }
        return locationPersonDtoList;
    }



    public Integer getLocationPersonCount(Long locationId, Integer personnlType){
        List<DictEntity> dictEntityList = dictService.getDictAndSubDictListById(personnlType);
        List<Integer> personnelTypeList = new ArrayList<>();
        for (DictEntity dictEntity : dictEntityList) {
            personnelTypeList.add(dictEntity.getId());
        }
        String sql = "select count(1) from v_personnel p " +
                "  left join poi_location  t on st_contains(t.location_polygon, p.pos) " +
                "  where p.is_enabled = 1 and p.floor_number = t.floor_number  and t.id = :locationId and p.personnel_type in :personnelTypeList";
        return ((Number) entityManager.createNativeQuery(sql)
                .setParameter("locationId", locationId)
                .setParameter("personnelTypeList", personnelTypeList)
                .getSingleResult()).intValue();
    }

    @Override
    public LocationAlarmDto exportLocationAlarmReport(Long locationId, Timestamp startTime, Timestamp endTime)  {
        PoiLocationEntity poiLocationEntity = poiLocationRepository.getById(locationId);
        LocationAlarmDto locationAlarmDto = new LocationAlarmDto();
        if(poiLocationEntity != null){
            locationAlarmDto.setLocationId(poiLocationEntity.getId());
            locationAlarmDto.setLocationName(poiLocationEntity.getLocationName());
            locationAlarmDto.setAlarmTypeCountDtoList(getAlarmRecordReport(locationId, startTime, endTime));
        }
        return locationAlarmDto;
    }



    public List<AlarmTypeCountDto> getAlarmRecordReport(Long locationId, Timestamp startTime, Timestamp endTime){
        String sql = "select alarm_type as alarmType,alarm_type_name as alarmTypeName ,count(1) as count from alarm_record where location_id = :locationId " +
                   "and  alarm_time BETWEEN :startTime and :endTime group by alarm_type,alarm_type_name";
        List<AlarmTypeCountDto> list =
                entityManager.createNativeQuery(sql)
                .unwrap(NativeQuery.class)
                .addScalar("alarmType", IntegerType.INSTANCE)
                .addScalar("alarmTypeName", StringType.INSTANCE)
                .addScalar("count", IntegerType.INSTANCE)
                .setParameter("locationId",locationId)
                .setParameter("startTime",startTime)
                .setParameter("endTime",endTime)
                .setResultTransformer(Transformers.aliasToBean(AlarmTypeCountDto.class))
                .list();
        return list;
    }

    @Override
    public List<LocationDeviceDto> exportLocationDevice() {
        List<LocationDeviceDto> locationDeviceDtoList = new ArrayList<>();
        List<PoiLocationEntity> poiLocationEntityList = poiLocationRepository.getAllByMapLevel(1);
        for(PoiLocationEntity poiLocationEntity : poiLocationEntityList){
            LocationDeviceDto locationDeviceDto = new LocationDeviceDto();
            locationDeviceDto.setLocationId(poiLocationEntity.getId());
            locationDeviceDto.setLocationName(poiLocationEntity.getLocationName());
            locationDeviceDto.setLocatorCount(getDeviceCount(poiLocationEntity.getId()));
            locationDeviceDtoList.add(locationDeviceDto);
        }
        return locationDeviceDtoList;
    }

    public Integer getDeviceCount(Long locationId){
        String sql = "select count(1) from d_locator p left join poi_location  t on st_contains(t.location_polygon, p.pos) " +
                " where p.is_enabled = 1 and p.floor_number = t.floor_number and t.id = :locationId";
        return ((Number) entityManager.createNativeQuery(sql)
                .setParameter("locationId", locationId)
                .getSingleResult()).intValue();
    }

    @Override
    public PersonnelMoveAnalyzeDto exportPersonnelMoveAnalyze(Long personnelId, Timestamp startTime, Timestamp endTime) {
        PersonnelView personnelView = personnelViewRepository.getById(personnelId);
        PersonnelMoveAnalyzeDto personnelMoveAnalyzeDto = new PersonnelMoveAnalyzeDto();
        if(personnelView != null){
            personnelMoveAnalyzeDto.setPersonnelId(personnelId);
            personnelMoveAnalyzeDto.setPersonnelName(personnelView.getPersonnelName());
            personnelMoveAnalyzeDto.setAlarmCountDtoList(getDayAlarmCount(personnelId, startTime, endTime));
            personnelMoveAnalyzeDto.setDayLocationMoveCountDtoList(getPeronnelMove(personnelId, startTime, endTime));
        }

        return personnelMoveAnalyzeDto;
    }

    public List<DayLocationMoveCountDto> getPeronnelMove(Long personnelId, Timestamp startTime, Timestamp endTime){
        List<DayLocationMoveCountDto> dayLocationMoveCountDtoList = new ArrayList<>();
        String sql = "select a.time,count(1) as count from " +
                "(select p.id,DATE(m.record_time) as time,count(1)" +
                " from personnel_move m " +
                " left join (select * from poi_location where map_level = 3) p " +
                " on st_contains(p.location_polygon, point(m.current_x, m.current_y)) and p.floor_number = m.current_floor_number" +
                " where m.personnel_id = :personnelId" +
                " and (m.record_time BETWEEN :startTime and :endTime) GROUP BY p.id,DATE(m.record_time)" +
                " order by time) a GROUP BY a.time";
        List<DayAlarmCountDto> list = entityManager.createNativeQuery(sql)
                .unwrap(NativeQuery.class)
                .addScalar("time", StringType.INSTANCE)
                .addScalar("count", IntegerType.INSTANCE)
                .setParameter("personnelId",personnelId)
                .setParameter("startTime",startTime)
                .setParameter("endTime",endTime)
                .setResultTransformer(Transformers.aliasToBean(DayAlarmCountDto.class))
                .list();
        if(list.size()>0 && list != null){
            for (DayAlarmCountDto dayAlarmCountDto : list){
                DayLocationMoveCountDto dayLocationMoveCountDto = new DayLocationMoveCountDto();
                String time = dayAlarmCountDto.getTime();
                dayLocationMoveCountDto.setTime(time);
                dayLocationMoveCountDto.setVisitLocationCount(dayAlarmCountDto.getCount());
                List<MoveInLocationDto> moveList = roomService.getPersonnelMoveLocationList(personnelId, time + " 00:00:00", time + " 23:59:59");
                if(moveList != null && moveList.size() > 0){
                    dayLocationMoveCountDto.setChangeLocationCount(moveList.size() - 1);
                }
                dayLocationMoveCountDtoList.add(dayLocationMoveCountDto);
            }
        }
        return dayLocationMoveCountDtoList;

    }


    public List<DayAlarmCountDto> getDayAlarmCount(Long personnelId, Timestamp startTime, Timestamp endTime){

        String sql = "select DATE(alarm_time) AS time,count(1) as count from alarm_record where personnel_id = :personnelId and " +
                "alarm_time BETWEEN :startTime and :endTime GROUP BY time ORDER BY time";

        List<DayAlarmCountDto> list = entityManager.createNativeQuery(sql)
                .unwrap(NativeQuery.class)
                .addScalar("time", StringType.INSTANCE)
                .addScalar("count", IntegerType.INSTANCE)
                .setParameter("personnelId",personnelId)
                .setParameter("startTime",startTime)
                .setParameter("endTime",endTime)
                .setResultTransformer(Transformers.aliasToBean(DayAlarmCountDto.class))
                .list();
        return list;
    }

    @Override
    public List<LocationInfoDto> getAllLocation() {
        List<LocationInfoDto> locationInfoDtoList = new ArrayList<>();
        List<DictEntity> dictEntityList = dictService.getLocationTypes();
        for(DictEntity dictEntity : dictEntityList){
            LocationInfoDto locationInfoDto = new LocationInfoDto();
            locationInfoDto.setLocationType(dictEntity.getId());
            locationInfoDto.setLocationTypeName(dictEntity.getDictName());
            locationInfoDto.setPid(dictEntity.getPid());
            locationInfoDto.setPoiLocationDtoList(getPoiLocation(dictEntity.getId()));
            locationInfoDtoList.add(locationInfoDto);
        }
        return locationInfoDtoList;
    }

    public List<PoiLocationDto> getPoiLocation(Integer locationType){
        List<PoiLocationDto> list = new ArrayList<>();
        List<PoiLocationEntity> poiLocationEntityList = poiLocationRepository.getAllByLocationType(locationType);
        if(poiLocationEntityList.size() > 0 && poiLocationEntityList != null){
            for (PoiLocationEntity poiLocationEntity : poiLocationEntityList){
                PoiLocationDto poiLocationDto = new PoiLocationDto();
                poiLocationDto.setLocationId(poiLocationEntity.getId());
                poiLocationDto.setLocationName(poiLocationEntity.getLocationName());

                list.add(poiLocationDto);
            }
        }
        return list;
    }

    @Override
    public List<PersonnelAttendanceRecordEntity> exportPersonnelAttendance(Long personnelId, Timestamp startTime, Timestamp endTime) {
        if(personnelId != null){
            return  personnelAttendanceRecordRepository.getAllByPersonnelIdAndEnterTimeBetween(personnelId, startTime, endTime);
        }else{
            return  personnelAttendanceRecordRepository.getAllByEnterTimeBetween(startTime, endTime);
        }
    }


    @Scheduled(cron = "0 0 1 * * ?")
    //定时任务，每天凌晨1点钟执行一次
//@Scheduled(cron = "0 43 16 * * ?")
    public void scheduleTask() throws UnsupportedEncodingException {
        doTask();
        logger.debug("do scheduleTask(entrance-exit) finish.");
    }

    public void doTask() throws UnsupportedEncodingException {
        Date yesterday = DateUtil.getOneDayBefore(new Date());
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String dateStr = dateFormat.format(yesterday);
        Date startTime = DateUtil.parseDateTimeStr(dateStr + " 00:00:01");
        Date stopTime = DateUtil.parseDateTimeStr(dateStr + " 23:59:59");
        String[] workTimeList = workTime.split(",");
        Date toWork = DateUtil.parseDateTimeStr(dateStr + " " + workTimeList[0]);
        Date offWork = DateUtil.parseDateTimeStr(dateStr + " " + workTimeList[1]);
        String location = new String(workPoi.getBytes("ISO8859-1"), StandardCharsets.UTF_8);
        PoiLocationEntity poiLocationEntity = poiLocationRepository.getByLocationName(location);
        List<PersonnelView> personnelViewList = personnelViewRepository.getAllByEnabledAndPersonnelType(1,104);
        for(PersonnelView personnelView : personnelViewList) {
            PersonnelAttendanceRecordEntity personnelAttendanceRecordEntity = new PersonnelAttendanceRecordEntity();
            Long personnelId = personnelView.getId();
            String personnelName = personnelView.getPersonnelName();
            List<PersonnelMoveEntity> moveList =
                    personnelMoveRepository.getAllByPersonnelIdAndRecordTimeBetween(personnelId, startTime, stopTime);
            if(moveList.size() > 0 && poiLocationEntity != null){
                for (PersonnelMoveEntity personnelMoveEntity : moveList) {
                    if (poiLocationEntity.getLocationPolygon().contains(
                            GeomUtil.INSTANCE.createPoint(personnelMoveEntity.getCurrentX(), personnelMoveEntity.getCurrentY()))) {
                        if (personnelAttendanceRecordEntity.getEnterTime() == null) {
                            personnelAttendanceRecordEntity.setEnterTime(personnelMoveEntity.getRecordTime());
                        }
                        personnelAttendanceRecordEntity.setLeaveTime(personnelMoveEntity.getRecordTime());
                    }
                }

//            Long maxId = personnelMoveRepository.getMaxId(personnelId, startTime, stopTime);
//            Long minId =  personnelMoveRepository.getMinId(personnelId, startTime, stopTime);
//            PersonnelMoveEntity maxPersonnelMove = personnelMoveRepository.getById(maxId);
//            PersonnelMoveEntity minPersonnelMove = personnelMoveRepository.getById(minId);
                if (personnelAttendanceRecordEntity.getEnterTime() != null && personnelAttendanceRecordEntity.getLeaveTime() != null) {
                    personnelAttendanceRecordEntity.setPersonnelId(personnelId);
                    personnelAttendanceRecordEntity.setPersonnelName(personnelName);
//                personnelAttendanceRecordEntity.setEnterTime(minPersonnelMove.getRecordTime());
//                personnelAttendanceRecordEntity.setLeaveTime(maxPersonnelMove.getRecordTime());
                    Long lateLength = (toWork.getTime() - personnelAttendanceRecordEntity.getEnterTime().getTime()) / 60000;
                    Long outTimeLength = (personnelAttendanceRecordEntity.getLeaveTime().getTime() - offWork.getTime()) / 60000;
                    personnelAttendanceRecordEntity.setLateLength(lateLength);
                    personnelAttendanceRecordEntity.setOutTimeLength(outTimeLength);
                    personnelAttendanceRecordRepository.save(personnelAttendanceRecordEntity);
                }
            }

        }
    }
}
